Analísis de Datos Exploratorio¶
IAGO GORDO OTERO Y JOSE MARÍA VEGA RAMIRO¶
Bank Account Fraud Dataset Suite¶
Este conjunto de datos contiene información y datos reales sobre la detección de fraudes en el momento de abrir una cuenta bancaria en línea. Con esta información podemos predecir y elaborar un modelo que nos guíe a la hora de conceder servicios financieros a clientes.
La variable objetivo es fraud_bool donde el 0 indica no fraude y el 1 fraude.
Descripción del problema¶
El objetivo es realizar todos los pasos hasta construir un modelo de Machine Learning que sea capaz de predecir la probabilidad de, si un cliente, en nuestro caso de una entidad financiera, es apropiado para concederle servicios financieros según la probabilidad de que pueda o no pueda hacer fraude.
Este enfoque proporcionará a las entidades financieras una herramienta poderosa para identificar con mayor precisión a los clientes con alto riesgo de cometer fraude, fortaleciendo así sus estrategias de prevención y protección financiera.
Pasos a realizar¶
1- Análisis de Datos: Iniciamos examinando los datos disponibles. Este paso implica entender su estructura, calidad y las relaciones entre las diferentes variables.
2- Transformaciones Necesarias: Basándonos en nuestro análisis, realizaremos las transformaciones apropiadas en los datos. Esto puede incluir la limpieza de datos (como eliminar valores atípicos o manejar valores faltantes), la normalización, y la ingeniería de características para hacer los datos más adecuados para el modelado.
3- Construcción de Modelos: Desarrollaremos varios modelos predictivos. Esto podría incluir una variedad de enfoques, como regresión, árboles de decisión, entre otros, dependiendo de la naturaleza del problema y los datos.
4- Evaluación y Selección del Modelo: Compararemos el rendimiento de los distintos modelos utilizando métricas adecuadas para la tarea, como la precisión, recall,F1 etc. El objetivo es identificar el modelo que ofrezca las predicciones más precisas.
5- Implementación del Modelo Seleccionado: Una vez identificado el modelo más eficaz, lo utilizaremos para realizar predicciones en nuevos datos.
Importamos las librerías que necesitamos para la realización del análisis descriptivo exploratorio.
import pandas as pd
import numpy as np
import seaborn as sns
from matplotlib import pyplot as plt
import plotly.express as px
import scipy.stats as ss
import matplotlib.ticker as ticker
import warnings
from sklearn.impute import KNNImputer
import warnings
from scipy.stats import chi2_contingency
pd.set_option('display.max_columns', 500)
pd.set_option('display.max_rows', 5000)
Definimos las funciones que usaremos durante el EDA.
def plot_feature(df, col_name, isContinuous, target):
"""
Visualize a variable with and without faceting on the target status (fraud_bool).
- df: DataFrame.
- col_name: Name of the variable in the DataFrame.
- isContinuous: True if the variable is continuous, False otherwise.
- target: Name of the target column to facet on (fraud_bool).
"""
f, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(12, 3), dpi=90)
count_null = df[col_name].isnull().sum()
color = '#5975A4'
if isContinuous:
sns.histplot(df.loc[df[col_name].notnull(), col_name], kde=False, ax=ax1)
else:
sns.countplot(x=col_name, data=df, ax=ax1, color=color)
ax1.set_title(f"{col_name} (Null count: {count_null})")
ax1.set_xlabel(col_name)
ax1.set_ylabel('Count')
if isContinuous:
sns.boxplot(x=col_name, y=target, hue=target, data=df, ax=ax2)
ax2.set_title(f"{col_name} by {target}")
else:
proportions = (df.groupby(col_name)[target].value_counts(normalize=True)
.rename('proportion')
.reset_index())
sns.barplot(x=col_name, y='proportion', hue=target, data=proportions, ax=ax2)
ax2.set_title(f"{col_name} by {target} proportion")
ax2.set_ylabel('Proportion')
ax2.set_xlabel(col_name)
plt.setp(ax1.xaxis.get_majorticklabels(), rotation=90)
plt.setp(ax2.xaxis.get_majorticklabels(), rotation=90)
plt.tight_layout()
plt.show()
def get_corr_matrix(dataset = None, metodo='pearson', size_figure=[10,8]):
if dataset is None:
print(u'\nHace falta pasar argumentos a la función')
return 1
sns.set(style="white")
corr = dataset.corr(method=metodo)
for i in range(corr.shape[0]):
corr.iloc[i, i] = 0
f, ax = plt.subplots(figsize=size_figure)
sns.heatmap(corr, center=0,
square=True, linewidths=.5, cmap ='viridis' ) #cbar_kws={"shrink": .5}
plt.show()
return 0
def get_deviation_of_mean_perc(df, list_var_continuous, target, multiplier):
pd_final = pd.DataFrame()
for i in list_var_continuous:
series_mean = df[i].mean()
series_std = df[i].std()
std_amp = multiplier * series_std
left = series_mean - std_amp
right = series_mean + std_amp
size_s = df[i].size
perc_goods = df[i][(df[i] >= left) & (df[i] <= right)].size/size_s
perc_excess = df[i][(df[i] < left) | (df[i] > right)].size/size_s
if perc_excess>0:
pd_concat_percent = pd.DataFrame(df[target][(df[i] < left) | (df[i] > right)]\
.value_counts(normalize=True).reset_index()).T
pd_concat_percent.columns = [pd_concat_percent.iloc[0,0],
pd_concat_percent.iloc[0,1]]
pd_concat_percent = pd_concat_percent.drop('fraud_bool',axis=0)
pd_concat_percent['variable'] = i
pd_concat_percent['sum_outlier_values'] = df[i][(df[i] < left) | (df[i] > right)].size
pd_concat_percent['porcentaje_sum_null_values'] = perc_excess
pd_final = pd.concat([pd_final, pd_concat_percent], axis=0).reset_index(drop=True)
if pd_final.empty:
print('No existen variables con valores nulos')
return pd_final
def get_percent_null_values_target(pd_loan, list_var_continuous, target):
pd_final = pd.DataFrame()
for i in list_var_continuous:
if pd_loan[i].isnull().sum()>0:
pd_concat_percent = pd.DataFrame(pd_loan[target][pd_loan[i].isnull()]\
.value_counts(normalize=True).reset_index()).T
pd_concat_percent.columns = [pd_concat_percent.iloc[0,0],
pd_concat_percent.iloc[0,1]]
pd_concat_percent = pd_concat_percent.drop('index',axis=0)
pd_concat_percent['variable'] = i
pd_concat_percent['sum_null_values'] = pd_loan[i].isnull().sum()
pd_concat_percent['porcentaje_sum_null_values'] = pd_loan[i].isnull().sum()/pd_loan.shape[0]
pd_final = pd.concat([pd_final, pd_concat_percent], axis=0).reset_index(drop=True)
if pd_final.empty:
print('No existen variables con valores nulos')
return pd_final
def cramers_v(confusion_matrix):
chi2 = ss.chi2_contingency(confusion_matrix)[0]
n = confusion_matrix.sum()
phi2 = chi2 / n
r, k = confusion_matrix.shape
phi2corr = max(0, phi2 - ((k-1)*(r-1))/(n-1))
rcorr = r - ((r-1)**2)/(n-1)
kcorr = k - ((k-1)**2)/(n-1)
return np.sqrt(phi2corr / min((kcorr-1), (rcorr-1)))
def duplicate_columns(frame):
groups = frame.columns.to_series().groupby(frame.dtypes).groups
dups = []
for t, v in groups.items():
cs = frame[v].columns
vs = frame[v]
lcs = len(cs)
for i in range(lcs):
ia = vs.iloc[:,i].values
for j in range(i+1, lcs):
ja = vs.iloc[:,j].values
if np.array_equal(ia, ja):
dups.append(cs[i])
break
return dups
def calcular_missing_por_fila(fila):
missing_count = 0
for col in fila.index:
if col == 'intended_balcon_amount' and fila[col] < 0:
missing_count += 1
elif fila[col] == -1:
missing_count += 1
return missing_count
def cramers_V(var1,var2) :
crosstab = np.array(pd.crosstab(var1,var2, rownames=None, colnames=None))
stat = chi2_contingency(crosstab)[0]
obs = np.sum(crosstab)
mini = min(crosstab.shape)-1
return (stat/(obs*mini))
Leemos el dataset
pd_fraud = pd.read_csv("../data/Base.csv",low_memory=False)
pd_fraud
| fraud_bool | income | name_email_similarity | prev_address_months_count | current_address_months_count | customer_age | days_since_request | intended_balcon_amount | payment_type | zip_count_4w | velocity_6h | velocity_24h | velocity_4w | bank_branch_count_8w | date_of_birth_distinct_emails_4w | employment_status | credit_risk_score | email_is_free | housing_status | phone_home_valid | phone_mobile_valid | bank_months_count | has_other_cards | proposed_credit_limit | foreign_request | source | session_length_in_minutes | device_os | keep_alive_session | device_distinct_emails_8w | device_fraud_count | month | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 0.9 | 0.166828 | -1 | 88 | 50 | 0.020925 | -1.331345 | AA | 769 | 10650.765523 | 3134.319630 | 3863.647740 | 1 | 6 | CA | 185 | 0 | BA | 1 | 0 | 24 | 0 | 500.0 | 0 | INTERNET | 3.888115 | windows | 0 | 1 | 0 | 7 |
| 1 | 1 | 0.9 | 0.296286 | -1 | 144 | 50 | 0.005418 | -0.816224 | AB | 366 | 534.047319 | 2670.918292 | 3124.298166 | 718 | 3 | CA | 259 | 1 | BA | 0 | 0 | 15 | 0 | 1500.0 | 0 | INTERNET | 31.798819 | windows | 0 | 1 | 0 | 7 |
| 2 | 1 | 0.9 | 0.044985 | -1 | 132 | 40 | 3.108549 | -0.755728 | AC | 870 | 4048.534263 | 2893.621498 | 3159.590679 | 1 | 14 | CB | 177 | 1 | BA | 0 | 1 | -1 | 0 | 200.0 | 0 | INTERNET | 4.728705 | other | 0 | 1 | 0 | 7 |
| 3 | 1 | 0.9 | 0.159511 | -1 | 22 | 50 | 0.019079 | -1.205124 | AB | 810 | 3457.064063 | 4054.908412 | 3022.261812 | 1921 | 6 | CA | 110 | 1 | BA | 0 | 1 | 31 | 1 | 200.0 | 0 | INTERNET | 2.047904 | linux | 0 | 1 | 0 | 7 |
| 4 | 1 | 0.9 | 0.596414 | -1 | 218 | 50 | 0.004441 | -0.773276 | AB | 890 | 5020.341679 | 2728.237159 | 3087.670952 | 1990 | 2 | CA | 295 | 1 | BA | 1 | 0 | 31 | 0 | 1500.0 | 0 | INTERNET | 3.775225 | macintosh | 1 | 1 | 0 | 7 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 999995 | 0 | 0.6 | 0.192631 | -1 | 104 | 40 | 0.030592 | -1.044454 | AB | 804 | 7905.711839 | 8341.468557 | 4972.635997 | 1 | 8 | CA | 75 | 1 | BC | 1 | 1 | 25 | 0 | 200.0 | 0 | INTERNET | 8.511502 | linux | 1 | 1 | 0 | 4 |
| 999996 | 0 | 0.8 | 0.322989 | 148 | 9 | 50 | 1.628119 | -1.409803 | AC | 3306 | 5391.470463 | 4955.170808 | 5022.728108 | 0 | 2 | CC | 154 | 1 | BC | 1 | 1 | -1 | 0 | 200.0 | 0 | INTERNET | 8.967865 | windows | 0 | 1 | 0 | 4 |
| 999997 | 0 | 0.8 | 0.879403 | -1 | 30 | 20 | 0.018563 | 34.692760 | AA | 1522 | 8063.102636 | 5670.654316 | 4377.196321 | 2023 | 6 | CF | 64 | 0 | BC | 0 | 1 | 11 | 0 | 200.0 | 0 | INTERNET | 8.195531 | other | 0 | 1 | 0 | 4 |
| 999998 | 0 | 0.9 | 0.762112 | -1 | 189 | 20 | 0.015352 | 94.661055 | AA | 1418 | 8092.641762 | 3982.582204 | 4394.803296 | 1678 | 6 | CA | 163 | 0 | BA | 1 | 0 | 28 | 0 | 500.0 | 0 | INTERNET | 4.336064 | windows | 1 | 1 | 0 | 4 |
| 999999 | 0 | 0.2 | 0.697452 | -1 | 321 | 20 | 2.655916 | 9.908499 | AA | 951 | 6169.630036 | 3695.308261 | 4352.334543 | 2 | 12 | CA | 36 | 1 | BE | 0 | 1 | 15 | 0 | 200.0 | 0 | INTERNET | 6.717022 | linux | 0 | 1 | 0 | 4 |
1000000 rows × 32 columns
print(len(pd_fraud.columns))
32
Vemos los dintitos tipos de variables que tenemos y como su distribución.
pd_fraud.dtypes.sort_values().to_frame('feature_type').groupby(by = 'feature_type').size().to_frame('count').reset_index()
| feature_type | count | |
|---|---|---|
| 0 | int64 | 18 |
| 1 | float64 | 9 |
| 2 | object | 5 |
Vemos rápidamente la distribución de la variable objetivo y vemos que está muy desbalanceada a favor de el 0, que indica no fraude.
pd_fraud.fraud_bool.value_counts()
fraud_bool 0 988971 1 11029 Name: count, dtype: int64
Vemos las distintas columnas que componen en dataset
pd_fraud.columns
Index(['fraud_bool', 'income', 'name_email_similarity',
'prev_address_months_count', 'current_address_months_count',
'customer_age', 'days_since_request', 'intended_balcon_amount',
'payment_type', 'zip_count_4w', 'velocity_6h', 'velocity_24h',
'velocity_4w', 'bank_branch_count_8w',
'date_of_birth_distinct_emails_4w', 'employment_status',
'credit_risk_score', 'email_is_free', 'housing_status',
'phone_home_valid', 'phone_mobile_valid', 'bank_months_count',
'has_other_cards', 'proposed_credit_limit', 'foreign_request', 'source',
'session_length_in_minutes', 'device_os', 'keep_alive_session',
'device_distinct_emails_8w', 'device_fraud_count', 'month'],
dtype='object')
Al leer la información del Dataset vemos que hay columnas que son categóricas. Hacemos una lista con esas variables y las numéricas. Esto nos será útil para futuras operaciones.
lista_variables_categoricas = ["payment_type","employment_status","housing_status","email_is_free","phone_home_valid",
"phone_mobile_valid","has_other_cards","foreign_request","source","device_os","keep_alive_session"]
lista_variables_numericas = ["income", "name_email_similarity", "prev_address_months_count", "current_address_months_count", "customer_age", "days_since_request",
"intended_balcon_amount", "zip_count_4w", "velocity_6h", "velocity_24h", "velocity_4w", "bank_branch_count_8w", "date_of_birth_distinct_emails_4w",
"credit_risk_score", "bank_months_count", "proposed_credit_limit", "session_length_in_minutes", "device_distinct_emails_8w", "device_fraud_count",
"month"]
target="fraud_bool"
Vemos la cantidad de filas y columnas del test originalmente y después de borrar los duplicados.Como podemos observar es la misma cantidad, por lo que en nuestro Dataset no tenemos duplicados.
print(pd_fraud.shape, pd_fraud.drop_duplicates().shape)
(1000000, 32) (1000000, 32)
Observamos tambien si el dataset contiene columnas duplicadas utilizando la función duplicate_columns. El restultado es que el dataset no contiene columnas duplicadas.
duplicate_cols = duplicate_columns(pd_fraud)
duplicate_cols
[]
pd_fraud.shape
(1000000, 32)
Vemos los valores únicos de cada columna para ver si estamos de acuerdo con la información sobre el dataset en referencia al tipo de dato que es cada columna.
Al ver los valores únicos concordamos con la información del dataset en relación a las variables que son categóricas y que son numéricas.
for i in pd_fraud.columns:
print(i, pd_fraud[i].unique())
fraud_bool [1 0]
income [0.9 0.3 0.7 0.6 0.4 0.2 0.8 0.1 0.5]
name_email_similarity [0.16682773 0.29628601 0.04498549 ... 0.87940313 0.76211215 0.69745243]
prev_address_months_count [ -1 92 172 28 289 35 55 194 103 27 34 58 61 12 57 53 96 30
111 26 86 64 18 234 97 56 54 125 110 65 217 93 126 25 33 80
99 45 104 87 49 62 282 101 135 154 36 14 91 83 10 32 51 325
333 159 11 318 109 152 288 102 336 202 225 118 115 319 201 47 37 241
60 13 165 66 94 95 98 59 29 89 16 304 211 314 22 31 63 321
141 112 52 151 142 9 50 173 90 307 106 78 24 195 310 8 146 284
85 44 160 23 100 38 48 327 140 105 122 67 127 21 119 7 156 179
88 107 82 42 145 324 39 306 339 209 294 72 79 246 114 84 137 46
188 220 139 74 312 239 75 20 161 168 290 68 187 303 116 230 297 77
203 181 177 41 291 123 124 108 295 308 133 183 278 43 121 130 328 113
189 300 222 199 70 196 216 355 15 81 315 117 131 298 157 76 40 213
338 237 164 178 320 252 192 197 148 132 138 208 285 311 134 153 171 190
166 193 120 309 316 305 231 253 155 280 235 287 347 205 71 19 273 232
343 301 144 191 143 335 221 174 326 262 329 332 215 170 342 331 150 228
218 149 182 323 286 313 163 185 206 147 341 247 351 243 344 223 128 251
352 219 269 180 175 184 236 158 176 69 226 238 317 322 129 136 293 296
210 302 245 162 345 244 279 264 227 167 353 330 186 334 73 265 275 169
276 260 363 242 299 250 198 256 229 212 224 254 274 17 340 233 292 6
346 267 207 371 350 240 358 200 272 361 277 214 283 357 266 248 348 204
257 337 281 362 377 364 354 261 263 258 349 249 381 359 255 268 271 259
270 356 374 5 360 366 365 372 367 369 375 383 370 373]
current_address_months_count [ 88 144 132 22 218 30 152 18 64 60 131 109 107 123 37 55 173 50
94 153 82 9 19 184 56 5 90 78 3 162 34 112 23 99 44 51
140 62 98 135 209 72 229 185 14 71 239 183 16 141 186 26 63 142
80 199 119 8 2 118 272 306 216 212 48 151 189 117 124 203 130 29
176 120 57 104 247 143 128 149 0 122 129 360 106 170 101 15 74 220
363 236 159 69 93 27 12 356 146 6 36 32 83 273 155 45 349 41
17 361 188 79 -1 171 111 138 68 169 175 47 39 160 113 13 7 270
25 274 296 377 38 35 54 89 105 11 156 167 33 194 290 145 31 381
215 43 136 61 178 42 85 84 58 49 102 76 103 177 1 73 126 70
139 181 250 366 125 121 137 147 66 154 328 52 232 295 100 87 257 65
320 180 4 164 249 20 67 40 114 193 278 150 195 81 77 127 248 225
244 242 297 97 95 224 148 24 234 227 222 158 190 86 91 172 96 59
198 182 46 217 174 75 165 108 133 204 134 219 321 206 53 10 163 316
240 291 214 317 230 255 243 157 197 28 233 116 110 246 277 92 115 208
281 301 251 282 372 357 191 289 269 373 205 376 207 298 354 369 370 285
374 166 238 161 201 241 228 358 362 283 235 231 200 179 265 384 304 338
21 226 254 293 310 262 340 192 347 319 309 353 202 331 302 288 280 367
333 352 387 196 276 322 187 213 268 266 223 210 284 267 256 371 311 313
275 299 359 271 279 253 308 245 334 211 312 221 258 382 260 259 346 261
237 326 368 168 364 263 303 292 307 264 375 337 343 365 252 315 287 329
355 342 314 325 300 380 345 324 294 344 335 383 327 336 323 318 351 379
286 339 378 305 350 389 330 332 348 341 385 399 398 386 388 391 390 425
407 392 393 395 410 394 397 396 401 412 404 402 400 417 424 406 408 418
419 403 411 405 428 416 413 414 409]
customer_age [50 40 30 20 70 60 80 10 90]
days_since_request [0.02092517 0.00541754 3.10854879 ... 0.01856287 0.01535159 2.65591566]
intended_balcon_amount [-1.33134496 -0.81622375 -0.7557277 ... 34.69276003 94.66105465
9.90849898]
payment_type ['AA' 'AB' 'AC' 'AD' 'AE']
zip_count_4w [ 769 366 870 ... 5906 6243 6189]
velocity_6h [10650.76552277 534.04731894 4048.53426315 ... 8063.10263573
8092.64176182 6169.63003565]
velocity_24h [3134.31963049 2670.91829173 2893.62149796 ... 5670.65431579 3982.58220422
3695.30826066]
velocity_4w [3863.64773953 3124.29816559 3159.59067885 ... 4377.19632143 4394.80329639
4352.33454309]
bank_branch_count_8w [ 1 718 1921 ... 2371 2339 2359]
date_of_birth_distinct_emails_4w [ 6 3 14 2 13 10 1 4 5 9 11 8 7 17 21 12 0 18 19 15 22 16 24 23
20 25 27 29 32 30 26 28 33 31 35 34 37 38 36 39]
employment_status ['CA' 'CB' 'CC' 'CD' 'CE' 'CF' 'CG']
credit_risk_score [ 185 259 177 110 295 199 272 83 222 118 229 95 296 172
119 12 302 181 234 115 308 201 125 299 275 319 220 106
112 251 108 145 50 171 103 166 189 24 215 236 223 320
214 269 105 113 193 143 278 155 205 49 111 317 161 148
257 98 281 190 158 169 238 211 227 228 276 273 290 117
194 129 123 77 130 216 121 282 359 241 191 197 219 206
151 180 285 163 249 200 233 224 204 127 192 154 132 91
237 164 114 202 209 186 81 182 245 264 339 221 64 304
218 183 328 93 43 137 142 316 178 85 305 167 314 97
140 247 92 149 53 338 128 175 235 107 104 198 176 102
187 291 300 254 287 173 326 89 133 88 318 99 101 138
170 62 289 21 265 203 134 70 136 303 195 345 271 69
242 232 213 343 122 75 270 307 48 32 297 208 4 100
45 184 147 324 159 60 165 80 226 210 298 329 82 16
74 131 243 301 225 87 162 188 322 277 280 274 109 139
126 90 66 250 76 240 61 79 152 283 341 231 78 153
258 37 116 248 96 63 286 230 262 263 57 246 94 58
309 146 279 179 160 156 323 86 252 71 344 306 356 55
239 207 40 351 84 72 288 336 266 255 261 174 124 244
212 168 14 38 293 294 312 196 120 267 15 157 292 141
260 340 253 311 135 46 346 352 28 56 325 268 67 354
217 310 331 150 333 256 315 327 51 65 8 68 25 364
332 337 19 348 284 29 144 52 335 321 59 73 42 39
11 44 33 -69 347 9 -21 35 -26 54 22 313 365 30
-42 47 334 -2 342 0 -38 -34 7 26 31 -55 353 20
2 -43 34 -16 23 5 18 -58 27 -28 36 -46 41 349
13 330 -20 -29 -86 -5 6 -73 -81 355 -1 -54 -45 -10
-63 -70 -83 -3 10 -35 -8 -85 -9 -91 -78 -62 -15 -7
357 375 -32 17 373 361 -103 -40 -75 -31 -33 -52 -51 -77
-48 -27 -44 -66 -114 -13 -11 -47 -61 -4 -6 -17 -30 -67
-24 -57 -39 -88 -36 1 -125 -93 -41 -14 -79 -12 -111 -64
-23 -92 -37 3 -25 369 -56 358 -18 362 -84 -50 -99 -65
360 371 -53 -89 350 -19 -80 -71 -68 -74 -107 -60 -87 374
-123 383 -72 -49 366 -22 -59 -82 -126 -115 -76 -147 -104 -96
-118 -121 -90 -95 363 -97 -100 -144 -136 367 378 -109 -94 -170
-116 380 368 -102 377 -129 -139 -98 -106 370 -117 -131 -105 -101
-127 -134 -110 -124 -113 -122 -153 -108 -149 -141 -135 -119 -165 -112
-164 -154 -137 -130 -128 -143 -140 -148 -132 -157 -133 -146 -145 -158
-120 -150 -168 376 -152 -138 385 -156 -166 -159 -169 -155 372 387
386 389 379 381 -167]
email_is_free [0 1]
housing_status ['BA' 'BB' 'BC' 'BD' 'BE' 'BF' 'BG']
phone_home_valid [1 0]
phone_mobile_valid [0 1]
bank_months_count [24 15 -1 31 30 28 25 1 11 22 9 21 5 10 2 19 27 4 3 20 26 29 6 14
16 7 12 18 23 32 13 8 17]
has_other_cards [0 1]
proposed_credit_limit [ 500. 1500. 200. 2000. 1900. 1000. 990. 490. 510. 190. 210. 2100.]
foreign_request [0 1]
source ['INTERNET' 'TELEAPP']
session_length_in_minutes [ 3.8881146 31.79881936 4.72870487 ... 8.19553116 4.33606391
6.71702157]
device_os ['windows' 'other' 'linux' 'macintosh' 'x11']
keep_alive_session [0 1]
device_distinct_emails_8w [ 1 2 0 -1]
device_fraud_count [0]
month [7 3 2 1 6 0 5 4]
Convertimos los tipos de columnas en diccionario. Es una forma de ver que tipo de dato es cada columna y si concuerda con la información que se nos proporciona en el dataset.
pd_fraud.dtypes.to_dict()
{'fraud_bool': dtype('int64'),
'income': dtype('float64'),
'name_email_similarity': dtype('float64'),
'prev_address_months_count': dtype('int64'),
'current_address_months_count': dtype('int64'),
'customer_age': dtype('int64'),
'days_since_request': dtype('float64'),
'intended_balcon_amount': dtype('float64'),
'payment_type': dtype('O'),
'zip_count_4w': dtype('int64'),
'velocity_6h': dtype('float64'),
'velocity_24h': dtype('float64'),
'velocity_4w': dtype('float64'),
'bank_branch_count_8w': dtype('int64'),
'date_of_birth_distinct_emails_4w': dtype('int64'),
'employment_status': dtype('O'),
'credit_risk_score': dtype('int64'),
'email_is_free': dtype('int64'),
'housing_status': dtype('O'),
'phone_home_valid': dtype('int64'),
'phone_mobile_valid': dtype('int64'),
'bank_months_count': dtype('int64'),
'has_other_cards': dtype('int64'),
'proposed_credit_limit': dtype('float64'),
'foreign_request': dtype('int64'),
'source': dtype('O'),
'session_length_in_minutes': dtype('float64'),
'device_os': dtype('O'),
'keep_alive_session': dtype('int64'),
'device_distinct_emails_8w': dtype('int64'),
'device_fraud_count': dtype('int64'),
'month': dtype('int64')}
En el diccionario anterior se observa que Python interpreto las columnas que eran categoricas como otro tipo de dato. Por ello realizamos un bucle que nos convierta esas columnas en datos categóricos.
for columna in lista_variables_categoricas:
pd_fraud[columna] = pd_fraud[columna].astype('category')
Convertimos nuestra columna objetivo, que es la que nos indica si hay o no hay fraude en string.
pd_fraud["fraud_bool"] = pd_fraud["fraud_bool"].astype(str)
Realizamos una lista con aquellas columnas que su tipo de dato es float. Además, aunque pueda sonar redundante, dentro de nuestro actual dataframe convertimos todas las columnas que están en esa lista a float.Eso nos da la certeza de que los datos están realmente como necesitamos. Estas conversiones serán importantes posteriormente a la hora de elaborar los gráficos.
list_var_continuous = list(pd_fraud.select_dtypes('float').columns)
pd_fraud[list_var_continuous] = pd_fraud[list_var_continuous].astype(float)
pd_fraud.dtypes
fraud_bool object income float64 name_email_similarity float64 prev_address_months_count int64 current_address_months_count int64 customer_age int64 days_since_request float64 intended_balcon_amount float64 payment_type category zip_count_4w int64 velocity_6h float64 velocity_24h float64 velocity_4w float64 bank_branch_count_8w int64 date_of_birth_distinct_emails_4w int64 employment_status category credit_risk_score int64 email_is_free category housing_status category phone_home_valid category phone_mobile_valid category bank_months_count int64 has_other_cards category proposed_credit_limit float64 foreign_request category source category session_length_in_minutes float64 device_os category keep_alive_session category device_distinct_emails_8w int64 device_fraud_count int64 month int64 dtype: object
Observamos que los cambios se han realizado correctamente.
pd_fraud.dtypes
fraud_bool object income float64 name_email_similarity float64 prev_address_months_count int64 current_address_months_count int64 customer_age int64 days_since_request float64 intended_balcon_amount float64 payment_type category zip_count_4w int64 velocity_6h float64 velocity_24h float64 velocity_4w float64 bank_branch_count_8w int64 date_of_birth_distinct_emails_4w int64 employment_status category credit_risk_score int64 email_is_free category housing_status category phone_home_valid category phone_mobile_valid category bank_months_count int64 has_other_cards category proposed_credit_limit float64 foreign_request category source category session_length_in_minutes float64 device_os category keep_alive_session category device_distinct_emails_8w int64 device_fraud_count int64 month int64 dtype: object
pd_fraud.dtypes.to_dict()
{'fraud_bool': dtype('O'),
'income': dtype('float64'),
'name_email_similarity': dtype('float64'),
'prev_address_months_count': dtype('int64'),
'current_address_months_count': dtype('int64'),
'customer_age': dtype('int64'),
'days_since_request': dtype('float64'),
'intended_balcon_amount': dtype('float64'),
'payment_type': CategoricalDtype(categories=['AA', 'AB', 'AC', 'AD', 'AE'], ordered=False, categories_dtype=object),
'zip_count_4w': dtype('int64'),
'velocity_6h': dtype('float64'),
'velocity_24h': dtype('float64'),
'velocity_4w': dtype('float64'),
'bank_branch_count_8w': dtype('int64'),
'date_of_birth_distinct_emails_4w': dtype('int64'),
'employment_status': CategoricalDtype(categories=['CA', 'CB', 'CC', 'CD', 'CE', 'CF', 'CG'], ordered=False, categories_dtype=object),
'credit_risk_score': dtype('int64'),
'email_is_free': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
'housing_status': CategoricalDtype(categories=['BA', 'BB', 'BC', 'BD', 'BE', 'BF', 'BG'], ordered=False, categories_dtype=object),
'phone_home_valid': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
'phone_mobile_valid': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
'bank_months_count': dtype('int64'),
'has_other_cards': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
'proposed_credit_limit': dtype('float64'),
'foreign_request': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
'source': CategoricalDtype(categories=['INTERNET', 'TELEAPP'], ordered=False, categories_dtype=object),
'session_length_in_minutes': dtype('float64'),
'device_os': CategoricalDtype(categories=['linux', 'macintosh', 'other', 'windows', 'x11'], ordered=False, categories_dtype=object),
'keep_alive_session': CategoricalDtype(categories=[0, 1], ordered=False, categories_dtype=int64),
'device_distinct_emails_8w': dtype('int64'),
'device_fraud_count': dtype('int64'),
'month': dtype('int64')}
Hacemos un conteo de los datos de fraud_bool para realizar posteriormente un gráfico. Así sabremos la proporción de Fraude y No fraude que tenemos. El 0 representa No fraude mientras que el 1 el Fraude.
Primero lo hacemos con porcentaje y después en valores absolutos.
pd_plot_fraud_bool = pd_fraud['fraud_bool']\
.value_counts(normalize=True)\
.mul(100).rename('percent').reset_index()
pd_plot_fraud_bool
| fraud_bool | percent | |
|---|---|---|
| 0 | 0 | 98.8971 |
| 1 | 1 | 1.1029 |
pd_plot_fraud_bool_conteo = pd_fraud['fraud_bool'].value_counts().reset_index()
pd_plot_fraud_bool_conteo
| fraud_bool | count | |
|---|---|---|
| 0 | 0 | 988971 |
| 1 | 1 | 11029 |
pd_plot_fraud_bool_pc = pd_plot_fraud_bool.merge(pd_plot_fraud_bool_conteo, on='fraud_bool', how='inner')
pd_plot_fraud_bool_pc
| fraud_bool | percent | count | |
|---|---|---|---|
| 0 | 0 | 98.8971 | 988971 |
| 1 | 1 | 1.1029 | 11029 |
Realizamos el gráfico y se puede observar tanto númericamente como graficamente que el porcentaje de Fraude es muy reducido en comparacion al de No fraude.
fig=px.bar(pd_plot_fraud_bool_pc,x='fraud_bool',y='percent')
fig.show()
Ahora vamos a analizar los valores nulos
En la documentacion podemos observar que existían variables con missing. Por ello realizamos una lista que contenga todas esas columnas para posteriormente tratarlas.
Todas esas columnas tienen como nulos los valores iguales a -1 menos 'intended_balcon_amount' que todos los valores negativos que tengan son missings. Hacemos una lista con esas columnas.
variables_con_missings=['prev_address_months_count','current_address_months_count','intended_balcon_amount', 'bank_months_count', 'session_length_in_minutes', 'device_distinct_emails_8w']
variables_con_missings
['prev_address_months_count', 'current_address_months_count', 'intended_balcon_amount', 'bank_months_count', 'session_length_in_minutes', 'device_distinct_emails_8w']
Realizamos dos listas con el objetivo de rellenarlas a través del bucle que se explicará a continuación. Con estas dos listas posteriormente haremos un dataframe.
missing_count = []
missing_percentage = []
Este bucle lo que realiza es contar el número de nulos que tiene cada columna en función del tipo de dato que sea nulo para cada una. Además luego realiza un porcentaje de nulos al comparar la cantidad resultante con la cantidad total.
for variable in variables_con_missings:
if variable == 'intended_balcon_amount':
missing_count.append(pd_fraud[pd_fraud[variable] < 0].shape[0])
else:
missing_count.append(pd_fraud[pd_fraud[variable] == -1].shape[0])
missing_percentage.append((missing_count[-1] / len(pd_fraud)) * 100)
missing_count
[712920, 4254, 742523, 253635, 2015, 359]
missing_percentage
[71.292, 0.4254, 74.2523, 25.3635, 0.20149999999999998, 0.0359]
Realizamos un data frame con los resultados obtenidos.
df_missing_data = pd.DataFrame({
'Variable': variables_con_missings,
'Missing': missing_count,
'Porcentaje Missing': missing_percentage
})
Ahora mismo estos nulos nos sirven para situarnos y saber que cantidad tenemos. Posteriormente a la hora de realizar el modelo veremos como los tratamos.
df_missing_data
| Variable | Missing | Porcentaje Missing | |
|---|---|---|---|
| 0 | prev_address_months_count | 712920 | 71.2920 |
| 1 | current_address_months_count | 4254 | 0.4254 |
| 2 | intended_balcon_amount | 742523 | 74.2523 |
| 3 | bank_months_count | 253635 | 25.3635 |
| 4 | session_length_in_minutes | 2015 | 0.2015 |
| 5 | device_distinct_emails_8w | 359 | 0.0359 |
Ahora haremos lo mismo pero pasa saber los missing por fila. Contaremos el numero de nulos en cada fila y luego lo dividiremos entre la el numero de columnas que tiene el dataframe.
De esta forma sabremos el porcentaje de missing que tenemos en cada fila.
missing_counts = pd_fraud.apply(calcular_missing_por_fila, axis=1)
missing_percentages = (missing_counts / len(pd_fraud.columns)) * 100
Elaboramos un dataframe para poder visualizarlo correctamente.
missing_data = pd.DataFrame({
'Row_Index': missing_counts.index,
'Missing_Count': missing_counts.values,
'Missing_Percentage': missing_percentages.values
})
missing_data
| Row_Index | Missing_Count | Missing_Percentage | |
|---|---|---|---|
| 0 | 0 | 2 | 6.250 |
| 1 | 1 | 2 | 6.250 |
| 2 | 2 | 3 | 9.375 |
| 3 | 3 | 2 | 6.250 |
| 4 | 4 | 2 | 6.250 |
| ... | ... | ... | ... |
| 999995 | 999995 | 2 | 6.250 |
| 999996 | 999996 | 2 | 6.250 |
| 999997 | 999997 | 1 | 3.125 |
| 999998 | 999998 | 1 | 3.125 |
| 999999 | 999999 | 1 | 3.125 |
1000000 rows × 3 columns
Como no podemos visualizar todo el dataset vamos a buscar el valor máximo de datos faltantes por fila para ver si hay alguna fila con mas de un 70 % de datos faltantes para proceder a eliminar debido a que no aportaría información. Como podemos observar el máximo es 15.6 % por lo que ninguna fila será eliminada por datos faltantes.
max_missing_percentage = missing_data['Missing_Percentage'].max()
max_missing_percentage
15.625
Elaboramos una copia del dataframe original para realizar gráficos ya que queremos hemos elaborados una función que además de hacer diferentes gráficos también nos cuenta el numero de nulos. Para ello deben estar los nulos como Nan.
pd_fraud_graficos = pd_fraud.copy()
for column in variables_con_missings:
if column == 'intended_balcon_amount':
pd_fraud_graficos.loc[pd_fraud_graficos[column] < 0, column] = np.nan
else:
pd_fraud_graficos[column].replace(-1, np.nan, inplace=True)
Comprobamos que ha hecho bien la conversión.
pd_fraud_graficos['prev_address_months_count'].isnull().sum()
712920
Calculamos el porcentaje de valores faltantes para cada variables en variables_con_missings, agrupados por la condición de fraude indicada en la columna fraud_bool. De esta forma podemos observar si ciertas características tienen más valores faltantes en transacciones fraudulentas comparadas con transacciones legítimas, o viceversa. Esto podría indicar que la falta de información en ciertas variables está de alguna manera asociada con actividades fraudulentas.
La alta tasa de valores faltantes para prev_address_months_count en transacciones fraudulentas (91.89%) en comparación con las transacciones no fraudulentas (71.06%) sugiere que la omisión de la información de la dirección anterior es más común en las transacciones fraudulentas. Esto puede ser un indicador de comportamiento sospechoso y podría ser un factor importante para detectar fraude.
El porcentaje de 88.38% de valores faltantes en transacciones fraudulentas indica que casi siempre falta la cantidad transferida inicialmente en estos casos, lo que es significativamente más alto que en las transacciones no fraudulentas (74.09%). Esto podría interpretarse como que en las transacciones fraudulentas, a menudo no se reporta o se reporta erróneamente la cantidad de la transacción, lo que puede ser una técnica utilizada para ocultar el fraude.
Por lo tanto, si teniamos la duda de eliminar prev_adress_months_count y intended_balcon_amount por su elevada cantidad de nulos llegamos a la conclusión de que no debemos eliminarla ya que eliminar esta variable podría hacer se pierda información valiosa que puede ser indicativa de comportamiento fraudulento.
missing_vals = pd.DataFrame()
for variable in variables_con_missings:
missing_vals_col = pd_fraud_graficos.groupby('fraud_bool')[variable].apply(lambda x: round(x.isna().sum()/len(x) * 100, 2))
missing_vals[variable] = missing_vals_col
print(missing_vals)
prev_address_months_count current_address_months_count \
fraud_bool
0 71.06 0.43
1 91.89 0.13
intended_balcon_amount bank_months_count \
fraud_bool
0 74.09 25.23
1 88.38 37.54
session_length_in_minutes device_distinct_emails_8w
fraud_bool
0 0.20 0.04
1 0.16 0.04
Realizamos un bucle utiliza la función plot_feature para generar visualizaciones de las diferentes variables del conjunto de datos , con un enfoque particular en su relación con la variable objetivo 'fraud_bool'.
La función plot_feature visualiza datos de un DataFrame en dos tipos de gráficos diferentes, dependiendo de si la variable es continua o categórica. Si la variable es continua, muestra un histograma y un diagrama de caja para ver la distribución y compararla con una variable objetivo. Si la variable es categórica, muestra un gráfico de conteo y un gráfico de barras de proporciones para ver cuántas veces aparece cada categoría y cómo se relaciona con la variable objetivo. También informa la cantidad de valores nulos.
Aplicaremos la función tanto en variables numéricas como categóricas, proporcionando una visión detallada de cómo cada variable se relaciona con la variable objetivo 'fraud_bool'.
for variable in lista_variables_numericas:
plot_feature(df=pd_fraud_graficos, col_name=variable,isContinuous=True,target='fraud_bool')
warnings.filterwarnings('ignore')
for variable in lista_variables_categoricas:
plot_feature(df=pd_fraud_graficos, col_name=variable,isContinuous=False,target='fraud_bool')
Vamos a comentar ahora los gráficos que nos aportan información mas relevante:
En el BoxPlot "income by fraud bool" podemos ver que las cuentas fraudulentas tienen una menor dispersión y que además la mediana en income es superior. Esto parece indicar que las cuentas que realizan fraude son aquellas con más income.
En el gráfico en el que se compara la variable "fraud_bool" con la variable "customer age" podemos observar que a medida que el rango de edad aumenta hay un fraude mayor, por lo que debería ser un factor a tener en cuenta.
Comparando el "name_email_similarity" se puede observar que cuando hacen fraude la similitud del email y del nombre del aplicante es menor que cuando no lo hacen.
En el gráfico "proposed_credit_limit by fraud bool" sugiere que en los casos de fraude los límites de crédito propuestos son más altos y con una mayor variabilidad en estos límites en comparación con los casos no fraudulentos. Sin embargo, hay ciertos casos no fraudulentos que tienen límites de crédito propuestos extremadamente altos, como lo indican los valores atípicos.
En el gráfico que compara "fraud bool" con "has_another card" lo que podemos observar es que en la mayoría de casos de fraude los individuos no poseían otra tarjeta de crédito, solamente una pequeña proporción de ellos poseían otra tarjeta.
Comparando "fraud_bool" con "device_os" se observa que Windows es el sistema operativo en el que se realiza más fraude.
El tiempo que pasan los usuarios en la página web del banco no es representativo de si se va a efectuar fraude o no.
En el boxplot que relaciona "intended_balcon_amount" con "fraud_bool" podemos observar que en los casos de fraude la cantidad que suelen ingresar al principio los aplicantes es menor que cuando no hay fraude. Además podemos observar muchos valores atípicos en ambos grupos, lo que indica que tanto para transacciones fraudulentas como para transacciones no fraudulentas pueden llegar a tener cantidades extremadamente altas o bajas.
Además al analizar source vemos que vemos que a la hora de hacer fraude está muy repartido si se hace a través de internet o teleapp, siendo gráficamente indistinguible.
Un gráfico que nos aporta mucha información es en el que se analiza la variable "housing_status" ya que podemos ver que la mayoría de personas que realizan fraude es cuando pertenecen a la categoria BA.
En "payment_type" se debe prestar especial atención a aquellos pagos que son hechos de forma AB, AC y AD.
Por último aquellas personas con un "employment_status" que sea igual a CC y CG realizan un mayor fraude según el gráfico realizado.
Con esta funcion elaboramos un dataframe que nos diga el número de outliers que hay en cada variable presente en la lista "list_var_continuous" y el porcentaje de outliers en relacion a la cantidad total de datos en cada columna.
get_deviation_of_mean_perc(pd_fraud, list_var_continuous, "fraud_bool", 3)
| 0 | 1 | variable | sum_outlier_values | porcentaje_sum_null_values | |
|---|---|---|---|---|---|
| 0 | 0.988242 | 0.011758 | days_since_request | 17775 | 0.017775 |
| 1 | 0.990295 | 0.009705 | intended_balcon_amount | 18960 | 0.018960 |
| 2 | 0.99355 | 0.00645 | velocity_6h | 4341 | 0.004341 |
| 3 | 0.996289 | 0.003711 | velocity_24h | 539 | 0.000539 |
| 4 | 0.870349 | 0.129651 | proposed_credit_limit | 6155 | 0.006155 |
| 5 | 0.979952 | 0.020048 | session_length_in_minutes | 23593 | 0.023593 |
Podemos observar que no son cantidades representativas,el mayor porcentaje es 2,3% . Sin embargo en función del tipo de modelo que escojamos decidiremos que hacer con estos atípicos debido a que algunos modelos son más sensibles a los datos atípicos que otros. Además los outliers pueden contener información muy valiosa en ocasiones.
Por ejemplo, en nuestro caso, en la detección de fraude , los outliers pueden ser precisamente los casos que se quieren identificar. Por ello, a la hora de realizar el modelo decidiremos que hacer con estos datos.
Aquí lo que queremos comprobar es las correlaciones que existen entre las distintas varibles numéricas. Utilizamos la funcion get_corr_matrix que generará una matriz de correlación a partir del conjunto de datos proporcionado.
get_corr_matrix(dataset = pd_fraud[lista_variables_numericas],
metodo='pearson', size_figure=[10,8])
0
Tal y como podemos observar las correlaciones entre las variables continuas no son del todo fuertes. La máxima correlacion que hay se encuentra en torno a 0,8 en valor absoluto. Sim embargo las siguientes que están en torno a 0,6 y 0,5 respectivamente, lo que nos indica, en el contexto práctico, que la correlacion es significativa y hay una asociación notable entre las variables, pero no lo suficientemente fuerte como para indicar que un cambio en una variable predecirá con alta precisión un cambio en la otra.
Destacar que la diagonal es 0 y no 1 porque se ha especificado así en la función para posteriormente al filtrar evitar ver los 1 que son de la variable consigo misma.
corr = pd_fraud[lista_variables_numericas].corr('pearson')
new_corr = corr.abs()
new_corr.loc[:,:] = np.tril(new_corr, k=-1)
new_corr = new_corr.stack().to_frame('correlation').reset_index().sort_values(by='correlation', ascending=False)
new_corr[new_corr['correlation']>0.5]
| level_0 | level_1 | correlation | |
|---|---|---|---|
| 372 | month | velocity_4w | 0.848100 |
| 313 | proposed_credit_limit | credit_risk_score | 0.606141 |
| 371 | month | velocity_24h | 0.549919 |
| 209 | velocity_4w | velocity_24h | 0.539115 |
Aquí indentificamos y ordenamos todas las correlaciones fuertes (mayores de 0.5 en valor absoluto) entre las variables continuas del conjunto de datos de entrenamiento, lo cual es útil para comprobar que lo que interpretamos en el gráfico de arriba visualmente es correcto y entender poder fijarnos en las dependencias más fuertes que haya entre las variables.
Ahora vamos a utilizar cramer-v para poder ver la asociacion que existe entre las variables categóricas y la variable objetivo. Aquí 0 indica ninguna asociación mientras que 1 es una asociación perfecta.
Interpretamos lo siguiente:
Payment type: Podemos observar que cuando hay más fraude es con el tipo de pago AC y que segun el índice de Cramér sugiere que hay una asociacion muy baja entre el tipo de pago y si la asocación es fraudulenta o no.
Employment_status: Cuando el employment_status es CA hay más fraude , sin embargo la asociación entre las variables sigue siendo debil.
Housing_status: Podemos observar que cuando el housing status es BG no existe fraude. Asociación débil.
Email_is_free: el tipo de email (gratuito o no) no es un indicador fuerte o decisivo de fraude en las transacciones.
Phone_home_valid y phone_mobile_valid: Se puede observar que la mayoria de fraude tiene lugar cuando no se da el número de teléfono de casa pero si el teléfono móvil.Sin embargo el índice cramer es muy bajo.
Has_other_cards:Se puede observar que cuando hay fraude la mayoria de usuarios que lo realizan no tienen otra tarjeta de crédito. Sin embargo según el índice de cramer se puede ver que conocer el valor de una variable no proporciona mucha información sobre el valor de la otra.
Device_os: No hay practicamente relacion entre las variables. Eso si, la mayoria de fraude viene de usuarios de Windows.
Source: La mayoria de fraude tiene lugar a traves de INTERNET, aunque tambien es verdad que la gran mayoria de usuarios proceden de Internet.
for var in lista_variables_categoricas:
confusion_matrix = pd.crosstab(pd_fraud["fraud_bool"], pd_fraud[var])
print(f"Matriz de confusión para {var}:")
print(confusion_matrix)
print(f"Valor de Cramér's V para {var}: {cramers_v(confusion_matrix.values)}")
print("\n")
Matriz de confusión para payment_type: payment_type AA AB AC AD AE fraud_bool 0 256885 366385 247862 117551 288 1 1364 4169 4209 1286 1 Valor de Cramér's V para payment_type: 0.039042842242482736 Matriz de confusión para employment_status: employment_status CA CB CC CD CE CF CG fraud_bool 0 721353 137335 36826 26422 22640 43949 446 1 8899 953 932 100 53 85 7 Valor de Cramér's V para employment_status: 0.039579041948826 Matriz de confusión para housing_status: housing_status BA BB BC BD BE BF BG fraud_bool 0 163318 259397 369855 25935 168553 1662 251 1 6357 1568 2288 226 582 7 1 Valor de Cramér's V para housing_status: 0.11487732983320799 Matriz de confusión para email_is_free: email_is_free 0 1 fraud_bool 0 466376 522595 1 3738 7291 Valor de Cramér's V para email_is_free: 0.027730004919501703 Matriz de confusión para phone_home_valid: phone_home_valid 0 1 fraud_bool 0 574685 414286 1 8238 2791 Valor de Cramér's V para phone_home_valid: 0.03510388236282846 Matriz de confusión para phone_mobile_valid: phone_mobile_valid 0 1 fraud_bool 0 108676 880295 1 1648 9381 Valor de Cramér's V para phone_mobile_valid: 0.013126368094879885 Matriz de confusión para has_other_cards: has_other_cards 0 1 fraud_bool 0 766914 222057 1 10098 931 Valor de Cramér's V para has_other_cards: 0.03513065449791526 Matriz de confusión para foreign_request: foreign_request 0 1 fraud_bool 0 964284 24687 1 10474 555 Valor de Cramér's V para foreign_request: 0.016824420425245186 Matriz de confusión para source: source INTERNET TELEAPP fraud_bool 0 982035 6936 1 10917 112 Valor de Cramér's V para source: 0.00373333855293764 Matriz de confusión para device_os: device_os linux macintosh other windows x11 fraud_bool 0 330997 53074 340754 256999 7147 1 1715 752 1974 6507 81 Valor de Cramér's V para device_os: 0.08046708891771352 Matriz de confusión para keep_alive_session: keep_alive_session 0 1 fraud_bool 0 415792 573179 1 7261 3768 Valor de Cramér's V para keep_alive_session: 0.05027680351314226
Realizamos lo mismo con un mapa de calor para ver ahora la asociación de las variables categóricas entre ellas y comprobamos que la asociación entre todas ellas es muy débil.
rows= []
for i1 in lista_variables_categoricas:
col = []
for i2 in lista_variables_categoricas:
cramers = cramers_V(pd_fraud[i1], pd_fraud[i2])
col.append(round(cramers,2))
rows.append(col)
cramers_results = np.array(rows)
v_c = pd.DataFrame(cramers_results, columns =lista_variables_categoricas , index = lista_variables_categoricas)
plt.figure(figsize=(8,6))
sns.heatmap(v_c, cmap="YlGnBu", annot=True, linewidth=.5)
plt.show()
Por último,cambiamos la variable objetivo a entero debido a que muchos modelos de machine-learning requiere que la variable objetivo sea numérica.
pd_fraud["fraud_bool"] = pd_fraud["fraud_bool"].astype(int)
pd_fraud.to_pickle('../data/pd_fraud.pkl')